using System;
using System.Collections.Generic;

namespace IQIGame.Onigao.Framework
{
    /// <summary>
    /// 对象池。
    /// </summary>
    /// <typeparam name="T">对象类型。</typeparam>
    public sealed class ObjectPool<T> : ObjectPoolBase where T : class, IPoolObject, new()
    {
        private Dictionary<string, List<T>> m_NameObjectsMap;
        private UniqueRefLinkedList<T> m_Objects;
        private readonly List<T> m_CachedToReleaseObjects;
        private float m_AutoReleaseInterval;
        private int m_SameResCapacity;
        private int m_TotalCapacity;
        private float m_ExpireTime;
        private float m_AutoReleaseTime;
        private IPoolObjectOperationHelper<T> m_OperationHelper;

        private IPoolObjectOperationHelper<T> OperationHelper
        {
            get
            {
                if (m_OperationHelper == null)
                {
                    m_OperationHelper = new DefualtObjectOperationHelper<T>();
                }
                return m_OperationHelper;
            }
        }

        /// <summary>
        /// 初始化对象池的新实例。
        /// </summary>
        /// <param name="id">对象池名称。</param>
        /// <param name="autoReleaseInterval">对象池自动释放可释放对象的间隔秒数。</param>
        /// <param name="sameResCapacity">预设相同的资源实例的容量</param>
        /// <param name="totalCapacity">对象池的总容量。</param>
        /// <param name="expireTime">对象池对象过期秒数。</param>
        public ObjectPool(int id, float autoReleaseInterval, int sameResCapacity, int totalCapacity, float expireTime)
        {
            Id = id;
            m_NameObjectsMap = new Dictionary<string, List<T>>();
            m_Objects = new UniqueRefLinkedList<T>();
            m_CachedToReleaseObjects = new List<T>();
            m_AutoReleaseInterval = autoReleaseInterval;
            m_SameResCapacity = sameResCapacity;
            m_TotalCapacity = totalCapacity;
            m_ExpireTime = expireTime;
            m_AutoReleaseTime = 0f;
        }

        public override int Id { get; }

        /// <summary>
        /// 获取对象池对象类型。
        /// </summary>
        public override Type ObjectType => typeof(T);

        /// <summary>
        /// 获取或设置对象池自动释放可释放对象的间隔秒数。
        /// </summary>
        public override float AutoReleaseInterval
        {
            get
            {
                return m_AutoReleaseInterval;
            }
            set
            {
                m_AutoReleaseInterval = value;
            }
        }

        /// <summary>
        /// 预设相同的资源实例的最大存储数量
        /// 如：场景中同一种怪物最大能存储的数量
        /// </summary>
        public override int SameResCapacity
        {
            get
            {
                return m_SameResCapacity;
            }
            set
            {
                if (value < 0)
                {
                    throw new FrameworkException("设置同类型资源最大容量失败，同类型资源最大容量不能小于0。");
                }

                if (m_SameResCapacity == value)
                {
                    return;
                }

                m_SameResCapacity = value;
                Release();
            }
        }

        /// <summary>
        /// 对象池的总容量。
        /// 如：所有怪物的最大存储数量
        /// </summary>
        public override int TotalCapacity
        {
            get
            {
                return m_TotalCapacity;
            }
            set
            {
                if (value < 0)
                {
                    throw new FrameworkException("设置总容量失败，总容量不能小于0。");
                }

                if (m_TotalCapacity == value)
                {
                    return;
                }

                m_TotalCapacity = value;
                Release();
            }
        }

        /// <summary>
        /// 获取或设置对象池对象过期秒数。
        /// </summary>
        public override float ExpireTime
        {
            get
            {
                return m_ExpireTime;
            }

            set
            {
                if (value < 0f)
                {
                    throw new FrameworkException("设置过期时间失败，过期时间不能小于0。");
                }

                if (ExpireTime == value)
                {
                    return;
                }

                m_ExpireTime = value;
                Release();
            }
        }

        public override ObjPoolTypeGroupKey FullName => ObjectPoolUtil.GetFullName(ObjectType, Id);

        /// <summary>
        /// 获取对象池中对象的数量。
        /// </summary>
        public override int TotalCount => m_Objects.Count;

        /// <summary>
        /// 池内现有单个类型资源（预设相同）数量
        /// </summary>
        /// <param name="assetName"></param>
        /// <returns></returns>
        public override int GetSpecificResCount(string assetName)
        {
            m_NameObjectsMap.TryGetValue(assetName, out var objects);
            return objects == null ? 0 : objects.Count;
        }

        /// <summary>
        /// 可不注册，不注册默认调用new T()来创建对象T
        /// 注意：如果要注册，必须在第一个对象创建之前！建议创建对象池后立即调用。
        /// </summary>
        /// <param name="factory"></param>
        public void RegisterFactory(IPoolObjectOperationHelper<T> factory)
        {
            m_OperationHelper = factory;
        }

        /// <summary>
        /// 获取对象（可取消）。
        /// </summary>
        /// <param name="assetName">对象资源路径。</param>
        /// <returns>要获取的对象，如果取消，返回null。</returns>
        public override IPoolObject GetObject(string assetName)
        {
            T obj = GetObjectFromPool(assetName);
            if (obj == null)
            {
                obj = OperationHelper.Create(assetName);
            }
            else
            {
                obj.OnGetFromPool();
            }
            return obj;
        }

        /// <summary>
        /// 回收对象。
        /// </summary>
        /// <param name="obj">要回收的对象。</param>
        public void PutBack(T obj)
        {
            if (obj == null)
            {
                throw new FrameworkException("回收对象失败，对象为空");
            }

            if (obj.isLoaded && TotalCount < m_TotalCapacity && GetSpecificResCount(obj.AssetName) < SameResCapacity)
            {
                obj.OnPutBackToPool();
                PutObjIntoPool(obj);
            }
            else
            {
                DestroyObject(obj);
            }
        }

        public override void PutBackObject(IPoolObject obj)
        {
            if (obj is not T)
            {
                throw new FrameworkException("对象类型和池子不匹配，不能放入该池。");
            }
            PutBack((T)obj);
        }

        private T GetObjectFromPool(string assetName)
        {
            T obj = null;
            m_NameObjectsMap.TryGetValue(assetName, out var objects);
            if (objects != null && objects.Count > 0)
            {
                int index = objects.Count - 1;
                obj = objects[index];
                objects.RemoveAt(index);

                m_Objects.Remove(obj);
            }
            return obj;
        }

        private void RemoveObjectFromPool(T obj)
        {
            m_NameObjectsMap[obj.AssetName].Remove(obj);
            m_Objects.Remove(obj);
        }

        private void PutObjIntoPool(T obj)
        {
            if (!m_NameObjectsMap.TryGetValue(obj.AssetName, out var objects))
            {
                objects = new List<T>();
                m_NameObjectsMap[obj.AssetName] = objects;
            }
            objects.Add(obj);
            m_Objects.AddLast(obj);
        }

        /// <summary>
        /// 释放对象池中过期的对象。
        /// </summary>
        /// <param name="toReleaseCount">尝试释放对象数量，如果大于已过期对象总数，则会强制释放一部分未过期未使用的。</param>
        /// <param name="releaseObjectFilterCallback">释放对象筛选函数。</param>
        public override void Release()
        {
            m_AutoReleaseTime = 0f;

            float timeSinceStart = UnityEngine.Time.realtimeSinceStartup;
            foreach (var obj in m_Objects)
            {
                float elapsedTime = timeSinceStart - obj.LastUseTime;
                if (elapsedTime > m_ExpireTime)
                {
                    m_CachedToReleaseObjects.Add(obj);
                }
            }
            if (m_CachedToReleaseObjects.Count <= 0)
            {
                return;
            }

            foreach (T toReleaseObject in m_CachedToReleaseObjects)
            {
                RemoveObjectFromPool(toReleaseObject);
                DestroyObject(toReleaseObject);
            }

            m_CachedToReleaseObjects.Clear();
        }

        /// <summary>
        /// 释放对象池中的所有对象。
        /// </summary>
        public override void ForceReleaseAll()
        {
            m_AutoReleaseTime = 0f;
            while (m_Objects.tail != null)
            {
                T element = m_Objects.RemoveFirst();
                DestroyObject(element);
            }
            foreach (var kv in m_NameObjectsMap)
            {
                kv.Value.Clear();
            }
            m_NameObjectsMap.Clear();
        }

        public override void OnUpdate(float deltaTime, float realElapseSeconds)
        {
            m_AutoReleaseTime += realElapseSeconds;
            if (m_AutoReleaseTime < m_AutoReleaseInterval)
            {
                return;
            }

            Release();
        }

        public override void Dispose()
        {
            ForceReleaseAll();
            OperationHelper.Dispose();
            m_OperationHelper = null;
        }

        private void DestroyObject(T obj)
        {
            OperationHelper.DestroyObject(obj);
        }
    }
}
